<?php
/**
 * CreateTime: 2023/1/11 11:32
 */

namespace App\Tools;

class SnowFlake
{
    private $datacenterId;  // 数据中心ID，占用5位，取值范围为0~31
    private $workerId;      // 工作机器ID，占用5位，取值范围为0~31
    private $sequence = 0;  // 序列号，占用12位，每毫秒最多可以生成4096个ID
    private $lastTimestamp = -1;  // 上次生成ID的时间戳

    const EPOCH = 1681119844068;  // 起始时间戳，用于减少ID的总长度

    public function __construct($datacenterId, $workerId)
    {
        if ($datacenterId < 0 || $datacenterId > 31) {
            throw new \Exception("Datacenter ID must be between 0 and 31");
        }
        if ($workerId < 0 || $workerId > 31) {
            throw new \Exception("Worker ID must be between 0 and 31");
        }
        $this->datacenterId = $datacenterId;
        $this->workerId = $workerId;
    }

    public function nextId()
    {
        $timestamp = $this->timeGen();
        if ($timestamp < $this->lastTimestamp) {
            throw new \Exception("Clock moved backwards. Refusing to generate ID for " . ($this->lastTimestamp - $timestamp) . " milliseconds.");
        }
        if ($timestamp == $this->lastTimestamp) {
            $this->sequence = ($this->sequence + 1) & 0xfff;  // 按位与运算，可以快速取模
            if ($this->sequence == 0) {
                $timestamp = $this->tilNextMillis($this->lastTimestamp);
            }
        } else {
            $this->sequence = 0;
        }
        $this->lastTimestamp = $timestamp;
        $id = (($timestamp - self::EPOCH) << 22) | ($this->datacenterId << 17) | ($this->workerId << 12) | $this->sequence;
        return $id;
    }

    private function timeGen()
    {
        return round(microtime(true) * 1000);
    }

    private function tilNextMillis($lastTimestamp)
    {
        $timestamp = $this->timeGen();
        while ($timestamp <= $lastTimestamp) {
            $timestamp = $this->timeGen();
        }
        return $timestamp;
    }
}